<?php

require_once("functions.php");

class DarwinCoreArchiveReader {
	private $archive_directory;
	private $idFlag = false;
	private $dwcFields = array(
			'Id'
		, 'GlobalUniqueIdentifier'
		, 'DateLastModified'
		, 'BasisOfRecord'
		, 'InstitutionCode'
		, 'CollectionCode'
		, 'CatalogNumber'
		, 'InformationWithheld'
		, 'Remarks'
		, 'ScientificName'
		, 'HigherTaxon'
		, 'Kingdom'
		, 'Phylum'
		, 'Class'
		, 'Order_'
		, 'Family'
		, 'Genus'
		, 'SpecificEpithet'
		, 'InfraspecificRank'
		, 'InfraspecificEpithet'
		, 'AuthorYearOfScientificName'
		, 'NomenclaturalCode'
		, 'IdentificationQualifier'
		, 'HigherGeography'
		, 'Continent'
		, 'WaterBody'
		, 'IslandGroup'
		, 'Island'
		, 'Country'
		, 'StateProvince'
		, 'County'
		, 'Locality'
		, 'MinimumElevationInMeters'
		, 'MaximumElevationInMeters'
		, 'MinimumDepthInMeters'
		, 'MaximumDepthInMeters'
		, 'CollectingMethod'
		, 'ValidDistributionFlag'
		, 'EarliestDateCollected'
		, 'LatestDateCollected'
		, 'DayOfYear'
		, 'Collector'
		, 'Sex'
		, 'LifeStage'
		, 'Attributes'
		, 'ImageURL'
		, 'RelatedInformation'
		, 'DecimalLatitude'
		, 'DecimalLongitude'
		, 'GeodeticDatum'
		, 'CoordinateUncertaintyInMeters'
		, 'PointRadiusSpatialFit'
		, 'FootprintWKT'
		, 'FootprintSpatialFit'
		, 'VerbatimCoordinates'
		, 'VerbatimLatitude'
		, 'VerbatimLongitude'
		, 'VerbatimCoordinateSystem'
		, 'GeoreferenceProtocol'
		, 'GeoreferenceSources'
		, 'GeoreferenceVerificationStatus'
		, 'GeoreferenceRemarks'
		, 'CatalogNumberNumeric'
		, 'IdentifiedBy'
		, 'DateIdentified'
		, 'CollectorNumber'
		, 'FieldNumber'
		, 'FieldNotes'
		, 'VerbatimCollectingDate'
		, 'VerbatimElevation'
		, 'VerbatimDepth'
		, 'Preparations'
		, 'TypeStatus'
		, 'GenBankNumber'
		, 'OtherCatalogNumbers'
		, 'RelatedCatalogedItems'
		, 'Disposition'
		, 'IndividualCount'
		);
/*
private $extraFields = array(
'GlobalUniqueIdentifier'
,'iucn_redlist_type'
,'lat_long_estimate'
,'lat_long_estimate_tried'
,'specimen_sheet_image'
,'iwmi'
,'common_name'
,'usda_plants_symbol'
,'accession'
,'relationship_to_locality'
,'text_1'
,'text_2 '
,'text_3'
,'text_4'
,'text_5'
,'text_6'
,'township_range_section'
,'biogeomancer'
);
*/
	private $extraFields = array(
			'Id'
		,	'GlobalUniqueIdentifier'
		,	'iucn_redlist_type'
		,	'lat_long_estimate'
		,	'lat_long_estimate_tried'
		,	'specimen_sheet_image'
		,	'iwmi'
		,	'text_1'
		,	'text_2'
		,	'text_3'
		,	'text_4'
		,	'common_name'
		,	'usda_plants_symbol'
		,	'accession'
	);
	private $dwcTable = 'dwc_record';
	private $extraTable = 'dwc_extra';

	function __construct($uri) {
		$this->load_archive($uri);
		$this->load_core_metadata();
	}
    
	function __destruct() {
//         $this->cleanup();
	}
    
	function load_archive($uri, $local = true) {
		$fileType = '';
		
		// currently only recognizing tarred and gzipped files
		if(!preg_match("/^(.*)\.(tgz|tar\.gz|tar\.gzip|zip)$/", $uri, $arr)) throw new Exception("DarwinCore Archive must be tar/gzip/zip");
		if (!$local) {
			$file_contents = get_remote_file($uri);
			if(!$file_contents) throw new Exception("Cannot access DarwinCore Archive at $uri");
		}
		$directory_name = $arr[1];

		$fileType = (preg_match("/^(.*)\.(zip)$/", $uri, $arr)) ? 'zip' : $fileType;

		# make temp dir
		$this->archive_directory = create_temp_dir();

		if($fileType == 'zip') {

			$temp_file_path = $this->archive_directory ."/dwca.zip";
			if ($local) {
//			var_dump(copy($uri,$temp_file_path));
	
				$zip = new ZipArchive;
				$res = $zip->open($uri);
				if ($res === TRUE) {
					$zip->extractTo($this->archive_directory);
					$zip->close();
				}
			}
	
		} else {
			# copy contents to temp gzip file
			$temp_file_path = $this->archive_directory ."/dwca.tar.gz";
		
			$TMP = fopen($temp_file_path, "w+");
			fwrite($TMP, $file_contents);
			fclose($TMP);
			
			# extract contents of temp gzip file to temp dir
			$output = shell_exec("tar -xzf $temp_file_path -C $this->archive_directory");
		}
	}
    
	function cleanup() {
		// remove tmp dir
		if($this->archive_directory) shell_exec("rm -fr $this->archive_directory");
	}
    
	function load_core_metadata() {
		$metadata_xml = get_hashed_response($this->archive_directory."/meta.xml");
		
		$this->core = $this->load_core_or_extension($metadata_xml->core);
//         if(strcasecmp($this->core->row_type, "http://rs.tdwg.org/dwc/terms/Taxon") != 0 && strcasecmp($this->core->row_type, "http://rs.tdwg.org/dwc/terms/DarwinCore") != 0) throw new Exception("Core row type must be http://rs.tdwg.org/dwc/terms/Taxon (".$this->core->row_type.")");
		
		$this->extensions = array();
		foreach($metadata_xml->extension as $extension_xml)
		{
				$extension_object = $this->load_core_or_extension($extension_xml);
				$this->extensions[strtolower($extension_object->row_type)] = $extension_object;
		}
	}
    
	function load_core_or_extension($metadata_xml) {
		$extension = new stdClass;
		
		// attributes
		$extension->row_type = import_decode(@$metadata_xml['rowType']);
		$extension->fields_terminated_by = import_decode(@$metadata_xml['fieldsTerminatedBy']);
		$extension->lines_terminated_by = import_decode(@$metadata_xml['linesTerminatedBy']);
		$extension->fields_enclosed_by = import_decode(@$metadata_xml['fieldsEnclosedBy']);
		$extension->compression = import_decode(@$metadata_xml['compression']);
		$extension->encoding = import_decode(@$metadata_xml['encoding']);
		$extension->ignore_header_lines = import_decode(@$metadata_xml['ignoreHeaderLines']);
		$extension->date_format = import_decode(@$metadata_xml['date_format']);
		
		// defaults
		if(!$extension->fields_terminated_by) $extension->core_field_terminator = "\t";
		if(!$extension->lines_terminated_by) $extension->core_line_terminator = "\n";
		if(!$extension->encoding) $extension->core_encoding = "ISO-8859-1";
		if(!$extension->ignore_header_lines) $extension->ignore_header_lines = 0;
		
		// SimpleXML turns \n into \\n which will confuse other things
		$extension->fields_terminated_by = self::convert_escaped_chars($extension->fields_terminated_by);
		$extension->lines_terminated_by = self::convert_escaped_chars($extension->lines_terminated_by);
		$extension->fields_enclosed_by = self::convert_escaped_chars($extension->fields_enclosed_by);
		
		// file location
		$extension->file_uri = (string) $metadata_xml->files->location;
		// the URI is relative so add the path to the temp directory
		if(strpos($extension->file_uri, "/") === false)
		{
				$extension->file_uri = $this->archive_directory ."/". $extension->file_uri;
		}
		
		// fields
		$extension->fields = array();
		$extension->constants = array();
		
		// grabbing the ID if it exists
		if($id = $metadata_xml->id)
		{
				$index = (int) @$id['index'];
				$term = (string) @$id['term'];
				$type = (string) @$id['type'];
				if(!$term) $term = "http://rs.tdwg.org/dwc/terms/taxonID";
				$extension->fields[$index] = array('term'      => $term,
																						'type'      => $type,
																						'default'   => '');
				$extension->id = $extension->fields[$index];
		}
		
		// grabbing the coreID if it exists
		if($id = $metadata_xml->coreid)
		{
				$index = (int) @$id['index'];
				$term = (string) @$id['term'];
				$type = (string) @$id['type'];
				if(!$term) $term = "http://rs.tdwg.org/dwc/terms/taxonID";
				$extension->fields[$index] = array('term'      => $term,
																						'type'      => $type,
																						'default'   => '');
				$extension->core_id = $extension->fields[$index];
		}
		
		// now all other fields
		foreach($metadata_xml->field as $field)
		{
				$index = (int) @$field['index'];
				$field_meta = array('term'      => (string) @$field['term'],
														'type'      => (string) @$field['type'],
														'default'   => (string) @$field['default']);
				if(isset($field['index'])) $extension->fields[$index] = $field_meta;
				else $extension->constants[] = $field_meta;
		}
		$extension->column_count = count($extension->fields);
		
		return $extension;
	}
    
/*
    function get_core_taxa()
    {
//         $file_contents = Functions::get_remote_file($this->core->file_uri);
        $file_contents = get_remote_file($this->core->file_uri);
        $lines = explode($this->core->lines_terminated_by, $file_contents);

        
        $all_taxa = array();
        $line_num = 0;
        foreach($lines as $line)
        {
            if($line_num%10000==0) echo "$line_num: ".memory_get_usage()."\n";
            $line_num++;
            if($this->core->ignore_header_lines && $line_num == 1) continue;
            if(!trim($line)) continue;
            $line = preg_replace("/^".preg_quote($this->core->fields_enclosed_by, "/")."/", "", $line);
            $line = preg_replace("/".preg_quote($this->core->fields_enclosed_by, "/")."$/", "", $line);
            $fields = explode($this->core->fields_enclosed_by.$this->core->fields_terminated_by.$this->core->fields_enclosed_by, $line);

//             if($fields) $all_taxa[] = $this->generate_taxon($fields, $this->core);
            if($fields) $all_taxa[] = $this->generate_taxon($fields);
            
            if($line_num%10000==1)
            {
                $count = count($all_taxa);
                print_r($all_taxa[$count-1]);
                //if($line_num>5) return $all_taxa;
            }
        }
        return $all_taxa;
    }
*/

	public function generateCoreSQL($sqlFile,$extraFile) {
	
		$dwcsql = ' INSERT INTO ' . $this->dwcTable . ' ( ' . implode(',',$this->dwcFields) . ' ) VALUES ';
		if(file_exists($sqlFile)) unlink($sqlFile);
		$fpsql = fopen($sqlFile,'a');
		fwrite($fpsql,$dwcsql);
	
		$extrasql = ' INSERT INTO ' . $this->extraTable . ' ( ' . implode(',',$this->extraFields) . ' ) VALUES ';
	
		if(file_exists($extraFile)) unlink($extraFile);
		$fpextra = fopen($extraFile,'a');
		fwrite($fpextra,$extrasql);
	
		$dwcstr = '';
		$extrastr = '';
		$first = true;
		$line_num = 0;
		$i = 0;
	
		if(strtolower(pathinfo($this->core->file_uri,PATHINFO_EXTENSION)) == 'csv') {
			$fp = fopen($this->core->file_uri,'r');
			if (($fp = fopen($this->core->file_uri, 'r')) !== FALSE) {
				while (($fields = fgetcsv($fp, '', $this->core->fields_terminated_by)) !== FALSE) {
					$line_num++;
					if($this->core->ignore_header_lines && $line_num == 1) {
						array_walk($fields,'ustrtolower');
						$this->csvFields = $fields;
						if(!in_array('id',$this->csvFields)) {
							$this->idFlag = true;
						}
						continue;
					}
	
					$data = $this->getDataArrays($fields);
					$data = $this->transformRow($data);
	
					if($this->idFlag) {
						$i++;
						$data['dwc']['id'] = $data['extra']['id'] = $i;
					}
	
	// 				$row = $this->generate_taxon($fields);
	// 
	// 				$row = $this->transformRow($row);
	
	
					$values = array();
					$writeFlag = true;
					if(!$first) {$dwcstr .= ",\r\n";$extrastr .= ",\r\n";}
					$first = false;
		
					foreach($this->dwcFields as $field) {
						$field = ($field == 'Order_') ? 'Order' : $field;
						$field = @strtolower($field);
						$values[] = "'" . $data['dwc'][$field] . "'";
					}
					$dwcstr .= ' ( ' . implode(',',$values) . ' ) ';
					unset($values);
	
					foreach($this->extraFields as $field) {
						$field = @strtolower($field);
						$values[] = "'" . $data['extra'][$field] . "'";
					}
					$extrastr .= ' ( ' . implode(',',$values) . ' ) ';
	
				if($line_num > 40) break;
		
					if($line_num > 50) {
						fwrite($fpsql,$dwcstr);
						$dwcstr = '';
						fwrite($fpextra,$extrastr);
						$extrastr = '';
	
						$line_num = 0;
						$writeFlag = false;
					}
				} # while
				if($writeFlag) {fwrite($fpsql,$dwcstr);fwrite($fpextra,$extrastr);}
				fclose($fpsql);fclose($fpextra);
			}
			fclose($fp);
			return true;
		} # type check
		return false;
	}

	private static function convert_escaped_chars($str) {
		// strcmp does a case insensitive string comparison
		if(strcasecmp($str, "\\n") == 0) return "\n";
		if(strcasecmp($str, "\\r") == 0) return "\r";
		if(strcasecmp($str, "\\t") == 0) return "\t";
		if(strcasecmp($str, "/n") == 0) return null;
		return $str;
	}

	public function getDataArrays($fields) {
		$dwcArray = array();
		$extraArray = array();
		foreach($this->dwcFields as $fld) {
			$fld = ($fld == 'Order_') ? 'order' : $fld;
			if(($index = array_search(@strtolower($fld),$this->csvFields)) !== FALSE) {
				if(isset($this->core->fields[$index])) {
					$meta = $this->core->fields[$index];
					$dwcArray[@strtolower($fld)] = ($fields[$index] == '') ? $meta['default'] : $fields[$index];
				}
			}
		}
		foreach($this->extraFields as $fld) {
			if(($index = array_search(@strtolower($fld),$this->csvFields)) !== FALSE) {
				$extraArray[@strtolower($fld)] = $fields[$index];
			}
		}
		return array('dwc' => $dwcArray, 'extra' => $extraArray);
	}

	public function generate_taxon($fields) {
		$row = array();
/*		for($i=0;$i<count($fields);$i++) {
			$meta = $this->core->fields[$i];
			$row[@basename($meta['term'])] = ($fields[$i] == '') ? $meta['default'] : $fields[$i];
		}*/
		for($i=0;$i<count($this->core->fields);$i++) {
			$meta = $this->core->fields[$i];
			$row[@strtolower(@basename($meta['term']))] = ($fields[$i] == '') ? $meta['default'] : $fields[$i];
		}
		return $row;
	}

	public function transformRow($rows) {
		$row = $rows['dwc'];
		$row['collectioncode'] = $row['institutioncode'];
		$row['institutioncode'] = 'UT';
		$row['globaluniqueidentifier'] = $row['institutioncode'] . ':' . $row['collectioncode'] . ':' . $row['catalognumber'];
		$rows['dwc'] = $row;
		$rows['extra']['globaluniqueidentifier'] = $row['globaluniqueidentifier'];
		return $rows;
	}

}

function ustrtolower(&$field) {
	$field = strtolower($field);
}

?>